#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/select.h>
#include <errno.h>
#include <time.h>
#include <string.h>

#include "hires.h"
#include "ts.h"
#include "sockets.h"

#define BUFSIZE 1024

int ck_long = 0;

int dbl_nl(const char * buf,int size){
	return buf[size-1]==10 && buf[size-2]==10;
}
int read_all(const char * buf,int size){
	return 0;
}

int close_connect(int create_socket) {
	close(create_socket);
}
int make_connect(const char *host,int port,const char *localaddr,int localport) {
	int create_socket;
	struct sockaddr_in address;
	double start,end;

	if(ck_long){
		start = hires();
	}

	if ((create_socket = socket(AF_INET,SOCK_STREAM,0)) <= 0) {
		perror("socket()\n");
		return 0;
	}

    if(localaddr && *localaddr){
		if(inet_pton(AF_INET, localaddr, &address.sin_addr)<=0){
			perror("inet_pton()");
			return 0;
		}
		address.sin_family = AF_INET;
		if(localport){
			address.sin_port = htons(localport);
		}else{
			address.sin_port = htons(0);
		}
		if (bind(create_socket,(struct sockaddr *)&address,sizeof(address)) != 0){
			printf("Not Binding Socket\n");
			return 0;
		}
	}

	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	if(inet_pton(AF_INET,host,&address.sin_addr)<=0){
		perror("inet_pton()");
		return 0;
	}

	if (connect(create_socket,(struct sockaddr *)&address,sizeof(address))){
		perror("Connect()");
		return 0;
	}

	if(ck_long){
		end = hires();
		if(end - start > 1.0){
			printf("Long request %s %.1f\n",timestamp(0),end - start);
		}
	}
	return create_socket;
}
int send_message(int create_socket,const char* message,int size_message){
	fd_set wfds;
	int retval;
	struct timeval tv;
	int rs;
	double start,end;

	if(ck_long){
		start = hires();
	}
	
	int need = size_message;

	while(need > 0){
		FD_ZERO(&wfds);
		FD_SET(create_socket, &wfds);
		tv.tv_sec = 30;
		tv.tv_usec = 0;
	
		retval = select(create_socket+1, NULL, &wfds, NULL, &tv);
		if(retval==-1){
			perror("write select()");
			close(create_socket);
			return 0;
		}else if(retval){
			rs = send(create_socket,message + (size_message-need),need,0);
			need -= rs;
		}else{
			close(create_socket);
			if(ck_long){
				printf("Timeout on write\n");
			}
			return 0;
		}
	}
	if(ck_long){
		end = hires();
		if(end - start > 1.0){
			printf("Long send %s %.1f\n",timestamp(0),end - start);
		}
	}
	return size_message;
}

int recv_message(int create_socket, char** response,int* size_response,check_eof* checker){
	fd_set rfds;
	int retval;
	struct timeval tv;
	int rs;
	int bufsize = BUFSIZE;
	char *buffer = (char*)malloc(bufsize);

	double start,end;

	if(ck_long){
		start = hires();
	}

	*response = buffer;
	int ready = 0;
	
	while(1){
		FD_ZERO(&rfds);
		FD_SET(create_socket, &rfds);
		tv.tv_sec = 30;
		tv.tv_usec = 0;

		retval = select(create_socket+1, &rfds, NULL, NULL, &tv);

		if (retval == -1){
			perror("read select()");
			close(create_socket);
			return 0;
		}else if(retval){
			if( FD_ISSET(create_socket, &rfds)){
				rs = recv(create_socket,buffer+ready,bufsize-ready,0);

				if(rs == -1){
					perror("read()");
					close(create_socket);
					return 0;
				}else if(!rs){
					*size_response = ready;
					close(create_socket);
					return ready;
				}
				ready += rs;
				if(bufsize - ready < 128){
					bufsize += BUFSIZE;
					buffer = (char*)realloc(buffer,bufsize);
					*response = buffer;
				}
				if(checker(buffer,ready)){
					if(ck_long){
						end = hires();
						if(end - start > 1.0){
							printf("Long recv %s %.1f\n",timestamp(0),end - start);
						}
					}
					*size_response = ready;
					return ready;
				}
			}else{
				if(ck_long){
					printf("Select return %d, but not for me\n",retval);
				}
				return 0;
			}
		}else{
			if(ck_long){
				printf("Timeout on read\n");
			}
			close(create_socket);
			return -1;
		}
	}
}
